#include "ByteBuffer.h"
#include "ByteBufferPool.h"

#include "Poco/ByteOrder.h"

using Poco::ByteOrder;

namespace cm_base
{
    static const int DEFAULT_SIZE = 4096;

    ByteBuffer::ByteBuffer() {
        start_ = 0;
        end_   = 0;
        size_  = DEFAULT_SIZE;
        bytes_ = new char[size_];

        m_parent = NULL;
        m_ref_cnt = 0;
    }

    ByteBuffer::ByteBuffer(size_t len) {
        start_ = 0;
        end_   = 0;
        if(len!=0){
            size_  = len;
        }else{
            size_ = DEFAULT_SIZE;
        }
        bytes_ = new char[size_];

        m_parent = NULL;
        m_ref_cnt = 0;
    }


    ByteBuffer::ByteBuffer(const char* bytes, size_t len) {
        start_ = 0;
        end_   = len;
        size_  = len;
        bytes_ = new char[size_];
        memcpy(bytes_, bytes, end_);

        m_ref_cnt = 0;
    }

    ByteBuffer::ByteBuffer(const char* bytes) {
        start_ = 0;
        end_   = strlen(bytes);
        size_  = end_;
        bytes_ = new char[size_];
        memcpy(bytes_, bytes, end_);

        m_parent = NULL;
        m_ref_cnt = 0;
    }

    ByteBuffer::~ByteBuffer() {
        delete bytes_;
    }

    bool ByteBuffer::ReadUInt8(UInt8& val) {
        return ReadBytes(reinterpret_cast<char*>(&val), 1);
    }

    bool ByteBuffer::ReadUInt16(UInt16& val) {
        UInt16 v;
        if (!ReadBytes(reinterpret_cast<char*>(&v), 2)) {
            return false;
        } else {
            val = ByteOrder::fromNetwork(v);
            return true;
        }
    }

    bool ByteBuffer::ReadUInt32(UInt32& val) {
        UInt32 v;
        if (!ReadBytes(reinterpret_cast<char*>(&v), 4)) {
            return false;
        } else {
            val = ByteOrder::fromNetwork(v);
            return true;
        }
    }

    bool ByteBuffer::ReadString(std::string& val, size_t len) {
        if (len > Length()) {
            return false;
        } else {
            val.append(bytes_ + start_, len);
            start_ += len;
            return true;
        }
    }

    bool ByteBuffer::ReadBytes(char* val, size_t len) {
        if (len > Length()) {
            return false;
        } else {
            memcpy(val, bytes_ + start_, len);
            start_ += len;
            return true;
        }
    }

    void ByteBuffer::WriteUInt8(UInt8 val) {
        WriteBytes(reinterpret_cast<const char*>(&val), 1);
    }

    void ByteBuffer::WriteUInt16(UInt16 val) {
        UInt16 v = ByteOrder::toNetwork(val);
        WriteBytes(reinterpret_cast<const char*>(&v), 2);
    }

    void ByteBuffer::WriteUInt32(UInt32 val) {
        UInt32 v = ByteOrder::toNetwork(val);
        WriteBytes(reinterpret_cast<const char*>(&v), 4);
    }

    void ByteBuffer::WriteString(const std::string& val) {
        WriteBytes(val.c_str(), val.size());
    }

    void ByteBuffer::WriteBytes(const char* val, size_t len) {
        if (Length() + len > Capacity())
            Resize(Length() + len);

        memcpy(bytes_ + end_, val, len);
        end_ += len;
    }

    void ByteBuffer::Resize(size_t size) {
        if (size > size_)
            size = _max(size, 3 * size_ / 2);

        size_t len = _min(end_ - start_, size);
        char* new_bytes = new char[size];
        memcpy(new_bytes, bytes_ + start_, len);
        delete [] bytes_;

        start_ = 0;
        end_   = len;
        size_  = size;
        bytes_ = new_bytes;
    }

    void ByteBuffer::Shift(size_t size) {
        if (size > Length())
            return;

        end_ = Length() - size;
        memmove(bytes_, bytes_ + start_ + size, end_);
        start_ = 0;
    }

    void ByteBuffer::reset()
    {
        start_ = 0;
        end_   = 0;
    }

    void ByteBuffer::increaseRef()
    {
        FastMutex::ScopedLock sl(m_mutex);
        ++m_ref_cnt;
        duplicate();
    }

    void ByteBuffer::decreaseRef()
    {
        FastMutex::ScopedLock sl(m_mutex);
        if( m_ref_cnt == 1 )
        {
            reset();
            if(m_parent)
            {
                m_parent->put( this );
            }
        }
        --m_ref_cnt;
        release();
    }

    ByteBufferPool* ByteBuffer::getParent()
    {
        return m_parent;
    }

    void ByteBuffer::setParent(ByteBufferPool* parent)
    {
        m_parent = parent;
    }

} // namespace cm_base
